﻿// ***********************************************************************
// Assembly         : Tx.OCC
// Author           : tianteng
// Created          : 03-31-2020
//
// Last Modified By : tianteng
// Last Modified On : 04-08-2020
// ***********************************************************************
// <copyright file="OCCTProxy.cs" company="北京腾雪科技有限责任公司">
//     Copyright © 北京腾雪科技有限责任公司 2022
// </copyright>
// <summary></summary>
// ***********************************************************************
using System;
using System.Windows.Forms;
using Occt;
using Tx;

namespace Tx.OCC.UI
{
    /// <summary>
    /// Class OCCTProxy.
    /// </summary>
    public class OcctProxy : IDisposable
    {
        /// <summary>
        /// My viewer
        /// </summary>
        public V3d_Viewer Viewer { get; private set; }

        /// <summary>
        /// My view
        /// </summary>
        public V3d_View View { get; private set; }

        /// <summary>
        /// My ais context
        /// </summary>
        public AIS_InteractiveContext Context { get; private set; }

        public OpenGl_GraphicDriver graphicDriver { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="OcctProxy"/> class.
        /// </summary>
        public OcctProxy(bool bWinForm = false)
        {
            if (bWinForm)
            {
                graphicDriver = new OpenGl_GraphicDriver(new Aspect_DisplayConnection());
            }
            else
            {
                graphicDriver = new D3DHost_GraphicDriver();
                graphicDriver.ChangeOptions.buffersNoSwap = true;
            }
            //GraphicDriver.ChangeOptions.contextDebug = true;

            Viewer = new V3d_Viewer(graphicDriver);
            Viewer.SetDefaultLights();
            Viewer.SetLightOn();

            // 背景色
            var color1 = System.Drawing.Color.FromArgb(172, 195, 237).ToQuantityColor();
            var color2 = System.Drawing.Color.FromArgb(205, 217, 232).ToQuantityColor();
            Viewer.SetDefaultBgGradientColors(color1, color2, Aspect_GradientFillMethod.Aspect_GFM_VER);
            // 格网
            Viewer.ActivateGrid(Aspect_GridType.Aspect_GT_Rectangular, Aspect_GridDrawMode.Aspect_GDM_Lines);

            Context = new AIS_InteractiveContext(Viewer);

            //显示视方体
            var view_cube = new AIS_ViewCube();
            var transform_pers = new Graphic3d_TransformPers(Graphic3d_TransModeFlags.Graphic3d_TMF_TriedronPers,
                Aspect_TypeOfTriedronPosition.Aspect_TOTP_RIGHT_UPPER,
                new Graphic3d_Vec2i(85, 85));
            view_cube.TransformPersistence = transform_pers;
            Context.Display(view_cube, true);

            Context.UpdateCurrentViewer();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (Context != null)
            {
                Context.Dispose();
                Context = null;
            }
            if (View != null)
            {
                View.Dispose();
                View = null;
            }
            if (graphicDriver != null)
            {
                graphicDriver.Dispose();
                graphicDriver = null;
            }

            if (Viewer != null)
            {
                Viewer.Dispose();
                Viewer = null;
            }
            GC.SuppressFinalize(this);
        }

        public const uint CS_OWNDC = 0x20;
        public const uint WS_POPUP = 0x80000000;
        /// <summary>
        /// Initializes the viewer.
        /// </summary>
        /// <param name="theWnd">The WND.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public bool NewWindow(IntPtr theWnd)
        {
            View = Viewer.CreateView();

            // 坐标轴
            View.TriedronDisplay(Aspect_TypeOfTriedronPosition.Aspect_TOTP_LEFT_LOWER,
                new Quantity_Color(Quantity_NameOfColor.Quantity_NOC_WHITE), 0.06, V3d_TypeOfVisualization.V3d_ZBUFFER);

            if (theWnd != IntPtr.Zero)
            {
                var wntWindow = new WNT_Window(theWnd);
                View.SetWindow(wntWindow);
                if (!wntWindow.IsMapped)
                    wntWindow.Map();
                wntWindow.Dispose();
            }
            else
            {
                var name = "OCC_Viewer" + Guid.NewGuid();
                var wClass = new WNT_WClass(new TCollection_AsciiString(name), IntPtr.Zero, CS_OWNDC);
                var wntWindow = new WNT_Window("OCC_Viewer", wClass, WS_POPUP, 64, 64, 64, 64);
                wntWindow.IsVirtual = true;
                View.SetWindow(wntWindow);
                wntWindow.Dispose();
            }

            View.Redraw();
            View.MustBeResized();
            return true;
        }

        /// <summary>
        /// Resizes custom FBO for Direct3D output.
        /// </summary>
        /// <param name="theWinSizeX">The win size x.</param>
        /// <param name="theWinSizeY">The win size y.</param>
        /// <returns>IntPtr.</returns>
        public IntPtr ResizeBridgeFBO(int theWinSizeX, int theWinSizeY)
        {
            var window = View.Window as WNT_Window;
            window.SetPos(0, 0, theWinSizeX, theWinSizeY);
            View.MustBeResized();
            View.Invalidate();
            return (View.View as D3DHost_View).D3dColorSurface();
        }

        /// <summary>
        /// Make dump of current view to file
        /// </summary>
        /// <param name="theFileName">Name of dump file</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public bool Dump(string theFileName)
        {
            if (View.IsNull)
                return false;
            View.Redraw();
            return View.Dump(theFileName);
        }

        /// <summary>
        /// Redraw view
        /// </summary>
        public void RedrawView()
        {
            if (!View.IsNull)
                View.Redraw();
        }

        /// <summary>
        /// Update view
        /// </summary>
        public void UpdateView()
        {
            if (View != null && !View.IsNull)
                View.MustBeResized();
        }

        /// <summary>
        /// Set computed mode in false
        /// </summary>
        public void SetDegenerateModeOn()
        {
            if (!View.IsNull)
            {
                View.ComputedMode = false;
                View.Redraw();
            }
        }

        /// <summary>
        /// Set computed mode in true
        /// </summary>
        public void SetDegenerateModeOff()
        {
            if (!View.IsNull)
            {
                View.ComputedMode = true;
                View.Redraw();
            }
        }

        /// <summary>
        /// Fit all
        /// </summary>
        /// <param name="theXmin">The xmin.</param>
        /// <param name="theYmin">The ymin.</param>
        /// <param name="theXmax">The xmax.</param>
        /// <param name="theYmax">The ymax.</param>
        public void WindowFitAll(int theXmin, int theYmin, int theXmax, int theYmax)
        {
            if (!View.IsNull)
                View.WindowFitAll(theXmin, theYmin, theXmax, theYmax);
        }

        /// <summary>
        /// Current place of window
        /// </summary>
        /// <param name="theX">The x.</param>
        /// <param name="theY">The y.</param>
        /// <param name="theZoomFactor">Current zoom</param>
        public void Place(int theX, int theY, float theZoomFactor)
        {
            var aZoomFactor = theZoomFactor;
            if (!View.IsNull)
                View.Place(theX, theY, aZoomFactor);
        }

        /// <summary>
        /// Set Zoom
        /// </summary>
        /// <param name="theX1">The x1.</param>
        /// <param name="theY1">The y1.</param>
        /// <param name="theX2">The x2.</param>
        /// <param name="theY2">The y2.</param>
        public void Zoom(int theX1, int theY1, int theX2, int theY2)
        {
            if (!View.IsNull)
                View.Zoom(theX1, theY1, theX2, theY2);
        }

        /// <summary>
        /// Set Pan
        /// </summary>
        /// <param name="theX">The x.</param>
        /// <param name="theY">The y.</param>
        public void Pan(int theX, int theY)
        {
            if (!View.IsNull)
                View.Pan(theX, theY);
        }

        /// <summary>
        /// Rotation
        /// </summary>
        /// <param name="theX">The x.</param>
        /// <param name="theY">The y.</param>
        public void Rotation(int theX, int theY)
        {
            if (!View.IsNull)
                View.Rotation(theX, theY);
        }

        /// <summary>
        /// Start rotation
        /// </summary>
        /// <param name="theX">The x.</param>
        /// <param name="theY">The y.</param>
        public void StartRotation(int theX, int theY)
        {
            if (!View.IsNull)
                View.StartRotation(theX, theY);
        }

        /// <summary>
        /// Select by rectangle
        /// </summary>
        /// <param name="theX1">The x1.</param>
        /// <param name="theY1">The y1.</param>
        /// <param name="theX2">The x2.</param>
        /// <param name="theY2">The y2.</param>
        //         public void Select(int theX1, int theY1, int theX2, int theY2)
        //         {
        //             if (!Context.IsNull)
        //             {
        //                 Context.Select(theX1, theY1, theX2, theY2, View, true);
        //             }
        //         }

        /// <summary>
        /// Select by click
        /// </summary>
        //         public void Select()
        //         {
        //             if (!Context.IsNull)
        //             {
        //                 Context.Select(true);
        //             }
        //         }

        /// <summary>
        /// Move view
        /// </summary>
        /// <param name="theX">The x.</param>
        /// <param name="theY">The y.</param>
        public void MoveTo(int theX, int theY)
        {
            if (!Context.IsNull && !View.IsNull)
            {
                Context.MoveTo(theX, theY, View, true);
            }
        }

        /// <summary>
        /// Select by rectangle with pressed "Shift" key
        /// </summary>
        /// <param name="theX1">The x1.</param>
        /// <param name="theY1">The y1.</param>
        /// <param name="theX2">The x2.</param>
        /// <param name="theY2">The y2.</param>
        //         public void ShiftSelect(int theX1, int theY1, int theX2, int theY2)
        //         {
        //             if (!Context.IsNull && !View.IsNull)
        //             {
        //                 Context.ShiftSelect(theX1, theY1, theX2, theY2, View, true);
        //             }
        //         }
        // 
        //         /// <summary>
        //         /// Select by "Shift" key
        //         /// </summary>
        //         public void ShiftSelect()
        //         {
        //             if (!Context.IsNull)
        //             {
        //                 Context.ShiftSelect(true);
        //             }
        //         }

        /// <summary>
        /// Set background color
        /// </summary>
        /// <returns>Quantity_Color.</returns>
        public Quantity_Color BackgroundColor()
        {
            return View.IsNull ? null : View.BackgroundColor;
        }

        /// <summary>
        /// Update current viewer
        /// </summary>
        public void UpdateCurrentViewer()
        {
            if (!Context.IsNull)
                Context.UpdateCurrentViewer();
        }

        /// <summary>
        /// Front side
        /// </summary>
        public void FrontView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_Yneg);
        }

        /// <summary>
        /// Top side
        /// </summary>
        public void TopView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_Zneg);
        }

        /// <summary>
        /// Left side
        /// </summary>
        public void LeftView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_Xneg);
        }

        /// <summary>
        /// Back side
        /// </summary>
        public void BackView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_Ypos);
        }

        /// <summary>
        /// Right side
        /// </summary>
        public void RightView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_Xpos);
        }

        /// <summary>
        /// Bottom side
        /// </summary>
        public void BottomView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_Zneg);
        }

        /// <summary>
        /// Axo side
        /// </summary>
        public void AxoView()
        {
            if (!View.IsNull)
                View.SetProj(V3d_TypeOfOrientation.V3d_XposYnegZpos);
        }

        /// <summary>
        /// Scale
        /// </summary>
        /// <returns>System.float.</returns>
        public float Scale()
        {
            return View.IsNull ? -1.0f : (float)View.Scale;
        }

        /// <summary>
        /// Zoom in all view
        /// </summary>
        public void ZoomAllView()
        {
            if (!View.IsNull)
            {
                View.FitAll();
                View.ZFitAll();
            }
        }

        /// <summary>
        /// Reset view
        /// </summary>
        public void Reset()
        {
            if (!View.IsNull)
                View.Reset();
        }

        /// <summary>
        /// Set display mode of objects
        /// </summary>
        /// <param name="theMode">Set current mode</param>
        public void SetDisplayMode(AIS_DisplayMode theMode)
        {
            if (Context.IsNull)
                return;

            if (Context.NbSelected == 0)
            {
                Context.SetDisplayMode(theMode, false);
            }
            else
            {
                for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
                {
                    Context.SetDisplayMode(Context.SelectedInteractive(), theMode, false);
                }
            }
            Context.UpdateCurrentViewer();
        }

        /// <summary>
        /// Set color
        /// </summary>
        /// <param name="color">The color.</param>
        public void SetColor(Quantity_Color color)
        {
            if (Context.IsNull)
                return;

            for (; Context.MoreSelected(); Context.NextSelected())
            {
                Context.SetColor(Context.SelectedInteractive(), color, false);
            }
            Context.UpdateCurrentViewer();
        }

        /// <summary>
        /// Get object color R/G/B
        /// </summary>
        /// <returns>Quantity_Color.</returns>
        public Quantity_Color ObjectColor()
        {
            if (Context.IsNull)
                return null;

            Context.InitSelected();
            if (!Context.MoreSelected())
                return null;

            var aCurrent = Context.SelectedInteractive();
            if (aCurrent.HasColor)
            {
                Context.Color(aCurrent, out var anObjCol);
                return anObjCol;
            }
            return null;
        }

        /// <summary>
        /// Set background color R/G/B
        /// </summary>
        /// <param name="color">The color.</param>
        public void SetBackgroundColor(Quantity_Color color)
        {
            if (!View.IsNull)
                View.SetBackgroundColor(color);
        }

        /// <summary>
        /// Erase objects
        /// </summary>
        public void EraseObjects()
        {
            if (Context.IsNull)
                return;

            Context.EraseSelected(false);
            Context.ClearSelected(true);
        }

        /// <summary>
        /// Get version
        /// </summary>
        /// <param name="theMaterial">The material.</param>
        public void SetMaterial(int theMaterial)
        {
            if (Context.IsNull)
                return;

            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                Context.SetMaterial(Context.SelectedInteractive(), new Graphic3d_MaterialAspect((Graphic3d_NameOfMaterial)theMaterial), false);
            }
            Context.UpdateCurrentViewer();
        }

        /// <summary>
        /// set transparency
        /// </summary>
        /// <param name="theTrans">The trans.</param>
        public void SetTransparency(int theTrans)
        {
            if (Context.IsNull)
                return;

            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                Context.SetTransparency(Context.SelectedInteractive(), theTrans / 10.0, false);
            }
            Context.UpdateCurrentViewer();
        }

        /// <summary>
        /// Return true if object is selected
        /// </summary>
        /// <returns><c>true</c> if [is object selected]; otherwise, <c>false</c>.</returns>
        public bool IsObjectSelected()
        {
            if (Context.IsNull)
                return false;

            Context.InitSelected();
            return Context.MoreSelected();
        }

        /// <summary>
        /// Return display mode
        /// </summary>
        /// <returns>System.Int32.</returns>
        public int DisplayMode()
        {
            if (Context.IsNull)
                return -1;

            bool isOneOrMoreInShading = false;
            bool isOneOrMoreInWireframe = false;
            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                if (Context.IsDisplayed(Context.SelectedInteractive(), AIS_DisplayMode.AIS_Shaded))
                {
                    isOneOrMoreInShading = true;
                }
                if (Context.IsDisplayed(Context.SelectedInteractive(), AIS_DisplayMode.AIS_WireFrame))
                {
                    isOneOrMoreInWireframe = true;
                }
            }
            if (isOneOrMoreInShading && isOneOrMoreInWireframe)
            {
                return 10;
            }
            else if (isOneOrMoreInShading)
            {
                return 1;
            }
            else if (isOneOrMoreInWireframe)
            {
                return 0;
            }
            return -1;
        }

        /// <summary>
        /// Set AISContext
        /// </summary>
        /// <param name="theViewer">The viewer.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public bool SetAISContext(OcctProxy theViewer)
        {
            this.Context = theViewer.Context;
            return this.Context != null || !this.Context.IsNull;
        }


        // ============================================
        // Import / export functionality
        // ============================================
        /// <summary>
        ///Import BRep file
        /// </summary>
        /// <param name="theFileName">Name of import file</param>
        public bool ImportBrep(string theFileName)
        {
            TopoDS_Shape aShape;
            if (!BRepTools.Read(out aShape, theFileName, new BRep_Builder()))
                return false;

            var aPrs = new AIS_Shape(aShape);
            Context.SetMaterial(aPrs, new Graphic3d_MaterialAspect(Graphic3d_NameOfMaterial.Graphic3d_NOM_GOLD), false);
            Context.SetDisplayMode(aPrs, AIS_DisplayMode.AIS_Shaded, false);
            Context.Display(aPrs, true);
            return true;
        }

        /// <summary>
        ///Import Step file
        /// </summary>
        /// <param name="theFileName">Name of import file</param>
        bool ImportStep(string theFileName)
        {
            var aReader = new STEPControl_Reader();
            if (aReader.ReadFile(theFileName) != IFSelect_ReturnStatus.IFSelect_RetDone)
            {
                return false;
            }

            bool isFailsonly = false;
            aReader.PrintCheckLoad(isFailsonly, IFSelect_PrintCount.IFSelect_ItemsByEntity);

            int aNbRoot = aReader.NbRootsForTransfer;
            aReader.PrintCheckTransfer(isFailsonly, IFSelect_PrintCount.IFSelect_ItemsByEntity);
            for (int aRootIter = 1; aRootIter <= aNbRoot; ++aRootIter)
            {
                aReader.TransferRoot(aRootIter);
                int aNbShap = aReader.NbShapes;
                if (aNbShap > 0)
                {
                    for (int aShapeIter = 1; aShapeIter <= aNbShap; ++aShapeIter)
                    {
                        Context.Display(new AIS_Shape(aReader.Shape(aShapeIter)), false);
                    }
                    Context.UpdateCurrentViewer();
                }
            }
            return true;
        }

        /// <summary>
        ///Import Iges file
        /// </summary>
        /// <param name="theFileName">Name of import file</param>
        bool ImportIges(string theFileName)
        {
            var aReader = new IGESControl_Reader();
            if (aReader.ReadFile(theFileName) != IFSelect_ReturnStatus.IFSelect_RetDone)
            {
                return false;
            }

            aReader.TransferRoots();
            TopoDS_Shape aShape = aReader.OneShape();
            Context.Display(new AIS_Shape(aShape), false);
            Context.UpdateCurrentViewer();
            return true;
        }

        /// <summary>
        ///Export BRep file
        /// </summary>
        /// <param name="theFileName">Name of export file</param>
        bool ExportBRep(string theFileName)
        {
            Context.InitSelected();
            if (!Context.MoreSelected())
            {
                return false;
            }

            var anIS = Context.SelectedInteractive() as AIS_Shape;
            return !anIS.IsNull
                 && BRepTools.Write(anIS.Shape, theFileName);
        }

        /// <summary>
        ///Export Step file
        /// </summary>
        /// <param name="theFileName">Name of export file</param>
        bool ExportStep(string theFileName)
        {
            var aType = STEPControl_StepModelType.STEPControl_AsIs;
            var aWriter = new STEPControl_Writer();
            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                var anIS = Context.SelectedInteractive() as AIS_Shape;
                if (anIS.IsNull)
                {
                    return false;
                }

                var aShape = anIS.Shape;
                if (aWriter.Transfer(aShape, aType) != IFSelect_ReturnStatus.IFSelect_RetDone)
                {
                    return false;
                }
            }
            return aWriter.Write(theFileName) == IFSelect_ReturnStatus.IFSelect_RetDone;
        }

        /// <summary>
        ///Export Iges file
        /// </summary>
        /// <param name="theFileName">Name of export file</param>
        bool ExportIges(string theFileName)
        {
            IGESControl_Controller.Init();
            var aWriter = new IGESControl_Writer(Interface_Static.CVal("XSTEP.iges.unit"), Interface_Static.IVal("XSTEP.iges.writebrep.mode"));
            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                var anIS = Context.SelectedInteractive() as AIS_Shape;
                if (anIS.IsNull)
                {
                    return false;
                }

                aWriter.AddShape(anIS.Shape);
            }

            aWriter.ComputeModel();
            return aWriter.Write(theFileName);
        }

        /// <summary>
        ///Export Vrml file
        /// </summary>
        /// <param name="theFileName">Name of export file</param>
        bool ExportVrml(string theFileName)
        {
            var aBuilder = new BRep_Builder();
            aBuilder.MakeCompound(out TopoDS_Compound aRes);
            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                var anIS = Context.SelectedInteractive() as AIS_Shape;
                if (anIS.IsNull)
                    return false;
                aBuilder.Add(aRes, anIS.Shape);
            }

            VrmlAPI_Writer aWriter = new VrmlAPI_Writer();
            aWriter.Write(aRes, theFileName);
            return true;
        }

        /// <summary>
        ///Export Stl file
        /// </summary>
        /// <param name="theFileName">Name of export file</param>
        bool ExportStl(string theFileName)
        {
            BRep_Builder aBuilder = new BRep_Builder();
            aBuilder.MakeCompound(out TopoDS_Compound aComp);
            for (Context.InitSelected(); Context.MoreSelected(); Context.NextSelected())
            {
                var anIS = Context.SelectedInteractive() as AIS_Shape;
                if (anIS.IsNull)
                {
                    return false;
                }
                aBuilder.Add(aComp, anIS.Shape);
            }

            StlAPI_Writer aWriter = new StlAPI_Writer();
            aWriter.Write(aComp, theFileName);
            return true;
        }

        /// <summary>
        ///Define which Import/Export function must be called
        /// </summary>
        /// <param name="theFileName">Name of Import/Export file</param>
        /// <param name="theFormat">Determines format of Import/Export file</param>
        /// <param name="theIsImport">Determines is Import or not</param>
        public bool TranslateModel(string theFileName, int theFormat, bool theIsImport)
        {
            bool isResult = false;
            var aFilename = theFileName;
            if (theIsImport)
            {
                switch (theFormat)
                {
                    case 0: isResult = ImportBrep(aFilename); break;
                    case 1: isResult = ImportStep(aFilename); break;
                    case 2: isResult = ImportIges(aFilename); break;
                }
            }
            else
            {
                switch (theFormat)
                {
                    case 0: isResult = ExportBRep(aFilename); break;
                    case 1: isResult = ExportStep(aFilename); break;
                    case 2: isResult = ExportIges(aFilename); break;
                    case 3: isResult = ExportVrml(aFilename); break;
                    case 4: isResult = ExportStl(aFilename); break;
                    case 5: isResult = Dump(aFilename); break;
                }
            }
            return isResult;
        }

        /// <summary>
        ///Initialize OCCTProxy
        /// </summary>
        //         void InitOCCTProxy()
        //         {
        //             myGraphicDriver.Nullify();
        //             myViewer.Nullify();
        //             myView.Nullify();
        //             Context.Nullify;
        //       }
    }
}
